#pragma once

#include <iostream>
#include <memory>
#include <elasticlient/client.h>
#include <json/json.h>
#include <cpr/cpr.h>
#include "logger.hpp"

// 实现字符串的序列化
bool Serialize(const Json::Value &val, std::string &body)
{
    std::stringstream ss;

    // 先实例化一个工厂类对象
    Json::StreamWriterBuilder swb;
    // 再使用工厂类对象来生产派生类
    std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
    int ret = sw->write(val, &ss);
    if(ret != 0)
    {
        std::cout << "Json Serialize failed!" << std::endl;
        return false;
    }

    body = ss.str();
    return true;
}

// 实现json字符串的反序列化
bool UnSerialize(const std::string &body, Json::Value &val)
{
    // 实例化工厂类对象
    Json::CharReaderBuilder crb;
    std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

    std::string errs;
    bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), &val, &errs);
    if(ret == false)
    {
        std::cout << "Json UnSerialize failed! " << errs << std::endl;
        return false;
    }

    return true;
}

// 构造索引
class ESIndex {
public:
    ESIndex(std::shared_ptr<elasticlient::Client> &client, const std::string &index_name, const std::string &type) 
        : _name(index_name), _type(type), _client(client)
    {
        Json::Value analysis;
        Json::Value analyzer;
        Json::Value ik;
        Json::Value tokenizer;
        tokenizer["tokenizer"] = "ik_max_word";
        ik["ik"] = tokenizer;
        analyzer["analyzer"] = ik;
        analysis["analysis"] = analyzer;
        _index["settings"] = analysis;
    }

    ESIndex& append(const std::string &key, const std::string &type = "text", const std::string &analyzer = "ik_max_word", bool enabled = true) {
        Json::Value fields;
        fields["type"] = type;
        fields["analyzer"] = analyzer;
        if (enabled == false) fields["enabled"] = enabled;
        _propertis[key] = fields;
        return *this;
    }

    bool create(const std::string &index_id = "default_index_id") {
        Json::Value mappings;
        mappings["dynamic"] = true;
        mappings["properties"] = _propertis;
        _index["mappings"] = mappings;

        std::string body;
        bool ret = Serialize(_index, body);
        if (ret == false) {
            LOG_ERROR("索引序列化失败!");
            return false;
        }

        // 发起搜索请求
        try {
            cpr::Response resp = _client->index(_name, _type, index_id, body);
            if (resp.status_code < 200 || resp.status_code >= 300) {
                LOG_ERROR("创建ES索引 {} 失败, 响应状态码异常: {}", _name, resp.status_code);
                return false;
            }
        } catch (std::exception &e) {
            LOG_ERROR("创建ES索引 {} 失败: {}", _name, e.what());
            return false;
        }
        return true;
    }

private:
    std::string _name;
    std::string _type;
    Json::Value _propertis;
    Json::Value _index;
    std::shared_ptr<elasticlient::Client> _client;
};

// 添加数据
class ESInsert {
public:
    ESInsert(std::shared_ptr<elasticlient::Client> &client, const std::string &index_name, const std::string &type) 
        : _name(index_name), _type(type), _client(client)
    {}

    ESInsert& append(const std::string &key, const std::string &val) {
        _item[key] = val;
        return *this;
    }

    bool insert(const std::string &id = "") {
        std::string body;
        bool ret = Serialize(_item, body);
        if (ret == false) {
            LOG_ERROR("索引序列化失败!");
            return false;
        }

        // 发起搜索请求
        try {
            cpr::Response resp = _client->index(_name, _type, id, body);
            if (resp.status_code < 200 || resp.status_code >= 300) {
                LOG_ERROR("新增数据 {} 失败, 响应状态码异常: {}", body, resp.status_code);
                return false;
            }
        } catch (std::exception &e) {
            LOG_ERROR("新增数据 {} 失败: {}", body, e.what());
            return false;
        }
        return true;
    }

private:
    std::string _name;
    std::string _type;
    Json::Value _item;
    std::shared_ptr<elasticlient::Client> _client;
};

// 删除数据
class ESRemove {
    public:
    ESRemove(std::shared_ptr<elasticlient::Client> &client, const std::string &index_name, const std::string &type) 
        : _name(index_name), _type(type), _client(client)
    {}

    bool remove(const std::string &id) {
        // 发起请求
        try {
            cpr::Response resp = _client->remove(_name, _type, id);
            if (resp.status_code < 200 || resp.status_code >= 300) {
                LOG_ERROR("删除数据 {} 失败, 响应状态码异常: {}", id, resp.status_code);
                return false;
            }
        } catch (std::exception &e) {
            LOG_ERROR("删除数据 {} 失败: {}", id, e.what());
            return false;
        }
        return true;
    }

private:
    std::string _name;
    std::string _type;
    std::shared_ptr<elasticlient::Client> _client;
};

// 搜索数据
class ESSearch {
public:
    ESSearch(std::shared_ptr<elasticlient::Client> &client, const std::string &index_name, const std::string &type) 
        : _name(index_name), _type(type), _client(client)
    {}

    // 必须不遵循的条件
    ESSearch& append_must_not_terms(const std::string &key, const std::vector<std::string> &vals) {
        Json::Value fields;
        for (const auto &val : vals) {
            fields[key].append(val);
        }
        Json::Value terms;
        terms["terms"] = fields;
        _must_not.append(terms);
        return *this;
    }

    ESSearch& append_should_match(const std::string &key, const std::string &val) {
        Json::Value field;
        field[key] = val;
        Json::Value match;
        match["match"] = field;
        _should.append(match);
        return *this;
    }

    Json::Value search() {
        Json::Value cond;
        if (!_must_not.empty()) cond["must_not"] = _must_not; 
        if (!_should.empty()) cond["should"] = _should; 
        Json::Value query;
        query["bool"] = cond;
        Json::Value root;
        root["query"] = query;

        std::string body;
        bool ret = Serialize(root, body);
        if (ret == false) {
            LOG_ERROR("索引序列化失败!");
            return Json::Value();
        }

        cpr::Response resp;
        // 发起搜索请求
        try {
            resp = _client->search(_name, _type, body);
            if (resp.status_code < 200 || resp.status_code >= 300) {
                LOG_ERROR("检索数据 {} 失败, 响应状态码异常: {}", body, resp.status_code);
                return Json::Value();
            }
        } catch (std::exception &e) {
            LOG_ERROR("检索数据 {} 失败: {}", body, e.what());
            return Json::Value();
        }

        // 反序列化响应正文
        Json::Value json_res;
        ret = UnSerialize(resp.text, json_res);
        if (ret == false) {
            LOG_ERROR("检索数据 {} 结果反序列化失败", resp.text);
            return Json::Value();
        }
        return json_res["hits"]["hits"];
    }

private:
    std::string _name;
    std::string _type;
    Json::Value _must_not;                          // 必须不遵循的条件
    Json::Value _should;                            // 应该遵循的条件
    std::shared_ptr<elasticlient::Client> _client;
};